package com.fit.utils.se;

import java.io.IOException;
import java.lang.reflect.Type;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

/**
 * java对象转json和json转java对象工具
 * 支持复杂的类型转换
 * 支付泛型转换
 * 依赖：jackson-core-2.7.0.jar、jackson-databind-2.7.0.jar、jackson-annotations-2.7.0.jar、stax-api-1.0-2.jar
 * 
 * 不需要依赖：jackson-mapper-asl、jackson-core-asl 在2.x版本中不在依赖此jar包
 * @author hcl
 *
 */
@SuppressWarnings("all")
public class JsonUtil {
	
	private static Logger log = LoggerFactory.getLogger(JsonUtil.class);
	
	private static ObjectMapper objectMapper = null;
	
	
	//初始化
	static{
		//json对象mapper
		objectMapper = new ObjectMapper();
		//json转换对象，如果json中没有这个字段 不报错 自动忽略
		objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
		objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
	}
	
	
	
	/**
	 * 将一个对象转换为json字符串
	 * @param obj 对象
	 * @return
	 */
	public static String toJson(Object obj){
		String json = null;
		try {
			json = objectMapper.writeValueAsString(obj);
		} catch (JsonProcessingException e) {
			log.error("对象转json异常", e);
		}
		return json;
	}
	
	
	
	/**
	 * 将一个json字符串转换为对象,支持泛型的转换
	 * @param json 字符串
	 * @param type type对象，type是class的父类
	 * @return
	 */
	public static <T>T jsonToObject(String json,Type type){
		T result = null;
		if(type instanceof Class){
			Class<T> clazz = (Class<T>)type;
			result = JsonUtil.jsonToObject(json, clazz);
		}else if(type instanceof ParameterizedTypeImpl){
			ParameterizedTypeImpl pt = (ParameterizedTypeImpl)type;
			JavaType javaType = JsonUtil.recursionGetClass(pt);
			try {
				return objectMapper.readValue(json, javaType);
			} catch (IOException e) {
				log.error("json转对象异常", e);
			}
		}else{
			log.error("json 转换 对象 失败！type:{} 不支持！",type.getClass());
		}
		return result;
	}
	
	
	/**
	 * 将一个json字符串转换为一个复杂带泛型的java对象
	 * 调用示例：jsonToObject(json,new TypeReference<User<String>(){});
	 * @param json 字符串
	 * @param typeReference 复杂类型
	 * @return
	 */
	public static <T>T jsonToObject(String json,TypeReference<T> typeReference){
		T result = null;
		try {
			result = objectMapper.readValue(json, typeReference);
		} catch (Exception e) {
			log.error("json转对象异常", e);
		}
		return result;
	}
	
	
	
//---------------------------------------------------------------------------------------------	
	
	
	/**
	 * 获取 Type 对象
	 * @param mainClazz 主要的Class
	 * @param elementClasses 泛型的Class
	 * @return
	 */
	private static JavaType getJavaType(Class<?> mainClazz, Class<?>... elementClasses) {
		if(elementClasses == null || elementClasses.length == 0){
			return objectMapper.getTypeFactory().constructSimpleType(mainClazz, null);
		}else{
			return objectMapper.getTypeFactory().constructParametricType(mainClazz, elementClasses);
		}
	} 
	
	
	
	
	/**
	 * 获取 Type 对象   
	 * @param mainClazz 主要的Class
	 * @param elementClasses 泛型的Class
	 * @return
	 */
	private static JavaType getJavaType(Class<?> mainClazz, JavaType... javaTypes) {
		if(javaTypes == null || javaTypes.length == 0){
			return objectMapper.getTypeFactory().constructSimpleType(mainClazz, null);
		}else{
			return objectMapper.getTypeFactory().constructParametricType(mainClazz, javaTypes); 
		}
	}  
	
	
	
	/**
	 * 递归获取 JavaType
	 * @param pt
	 * @return
	 */
	private static JavaType recursionGetClass(ParameterizedTypeImpl pt){
		Class clazz = pt.getRawType();
		
		Type[] types = pt.getActualTypeArguments();
		JavaType[] javaTypes = new JavaType[types.length];
		for(int i=0; i<types.length; i++){
			Type t = types[i];
			if(t instanceof Class){
				Class clazzt = (Class)t;
				javaTypes[i] = JsonUtil.getJavaType(clazzt, new Class[0]);
			}else if(t instanceof ParameterizedTypeImpl){
				ParameterizedTypeImpl pti = (ParameterizedTypeImpl)t;
				javaTypes[i] = JsonUtil.recursionGetClass(pti);
			}else{
				log.error("json 转换 对象 失败！type:{} 不支持！",t.getClass());
				return null;
			}
		}
		return JsonUtil.getJavaType(clazz, javaTypes);
	}
	
	
	
	/**
	 * 将一个json字符串转换为对象
	 * @param json
	 * @param clazz
	 * @return
	 */
	private static <T>T jsonToObject(String json,Class<T> clazz){
		Object val = null;
		if(json != null){
			try {
				//把json转换为基本类型对象
				val = JsonUtil.basicTypeTransformation(json, clazz);
				if(val == null && !json.trim().equals("")){
					//目标对象不为八大基本类型
					val = objectMapper.readValue(json, clazz);
				}
			} catch (Exception e) {
				log.error("json转对象异常",e);
			}
		}
		return (T)val;
	}
	

	
	
	/**
	 * 基本类型转换，如果clazz是基本类型返回转换后的值，否则返回null
	 * @param str 不能为空字符串，否则将抛异常
	 * @param clazz
	 * @return
	 */
	private static <T>T basicTypeTransformation(String str,Class<T> clazz){
		Object val = null;
		if(str == null){
			log.error("被转换的字符串不能为空！");
			return null;
		}
		if(clazz.getName().equals(String.class.getName())){
			val = str;
		}else if(clazz.getName().equals(Short.class.getName()) || clazz.getName().equals("short")){
			val = Short.parseShort(str);
		}else if(clazz.getName().equals(Integer.class.getName()) || clazz.getName().equals("int")){
			val = Integer.parseInt(str);
		}else if(clazz.getName().equals(Long.class.getName()) || clazz.getName().equals("long")){
			val = Long.parseLong(str);
		}else if(clazz.getName().equals(Float.class.getName()) || clazz.getName().equals("float")){
			val = Float.parseFloat(str);
		}else if(clazz.getName().equals(Double.class.getName()) || clazz.getName().equals("double")){
			val = Double.parseDouble(str);
		}else if(clazz.getName().equals(Character.class.getName()) || clazz.getName().equals("char")){
			if(str.length() >= 0){
				val = str.charAt(0);
			}
		}else if(clazz.getName().equals(Boolean.class.getName()) || clazz.getName().equals("boolean")){
			val = Boolean.parseBoolean(str);
		}else if(clazz.getName().equals(Byte.class.getName()) || clazz.getName().equals("byte")){
			val = Byte.parseByte(str);
		}
		return (T)val;
	}
}
